﻿// <copyright file="RangeAreaItemsDialogViewModel.cs" company="Oleg V. Polikarpotchkin">
// Copyright © 2008-2009 Oleg V. Polikarpotchkin. All Right Reserved
// </copyright>
// <author>Oleg V. Polikarpotchkin</author>
// <email>ov-p@yandex.ru</email>
// <date>2008-05-27</date>
// <summary>OpenWPFChart library. RangeAreaItemsDialog ViewModel.</summary>
// <revision>$Id$</revision>

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel; // For PropertyChangedEventArgs
using System.Diagnostics;
using System.Linq;
using System.Windows.Input;
using System.Windows.Media;
using OpenWPFChart.Parts;

namespace OpenWPFChart.Helpers
{
	/// <summary>
	/// RangeAreaItemsDialog ViewModel.
	/// </summary>
	class RangeAreaItemsDialogViewModel : INotifyPropertyChanged
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="RangeAreaItemsDialogViewModel"/> class.
		/// </summary>
		/// <param name="dataViewList">List of DataView objects.</param>
		public RangeAreaItemsDialogViewModel(IList<DataView> dataViewList)
		{
			DataViewCollection = new ObservableCollection<DataView>(dataViewList);
		}

		#region DataViewCollection
		ObservableCollection<DataView> dataViewCollection;
		/// <summary>
		/// Gets the data view collection.
		/// </summary>
		/// <value>The data view collection.</value>
		public ObservableCollection<DataView> DataViewCollection
		{
			get { return dataViewCollection; }
			private set
			{
				dataViewCollection = value;
				dataViewCollection.CollectionChanged += dataViewCollection_CollectionChanged;

				UpdateItemsCollection();
				UpdateRangeAreasCollection();
			}
		}

		/// <summary>
		/// Handles the CollectionChanged event of the DataViewCollection.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
		void dataViewCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			UpdateItemsCollection();
			UpdateRangeAreasCollection();
		}

		/// <summary>
		/// Updates the items collection.
		/// </summary>
		void UpdateItemsCollection()
		{
			ItemsCollection = (from item in dataViewCollection.OfType<ItemDataView>()
							   where isItemDataViewMappedToCurveVisual(item)
							   select item).ToList();
		}

		/// <summary>
		/// Determines whether the <paramref name="itemDataView"/> is mapped to curve visual.
		/// </summary>
		/// <param name="itemDataView">The item data view.</param>
		/// <returns>
		/// 	<c>true</c> if the <paramref name="itemDataView"/> is mapped to curve visual; 
		/// 	otherwise, <c>false</c>.
		/// </returns>
		static bool isItemDataViewMappedToCurveVisual(ItemDataView itemDataView)
		{
			if (itemDataView == null)
				return false;
			Type visualType = itemDataView.VisualCue as Type;
			if (visualType != null)
			{
				if (visualType.GetInterface("ICurveVisual") != null)
					return true;
			}
			else if (itemDataView.VisualCue is IEnumerable)
			{
				foreach (object item in itemDataView.VisualCue as IEnumerable)
				{
					visualType = item as Type;
					if (visualType != null)
					{
						if (visualType.GetInterface("ICurveVisual") != null)
							return true;
					}
				}
			}
			return false;
		}

		/// <summary>
		/// Updates the range areas collection.
		/// </summary>
		void UpdateRangeAreasCollection()
		{
			RangeAreasCollection = dataViewCollection.OfType<RangeAreaDataView>().ToList();
		}
		#endregion DataViewCollection

		#region Items
		IList<ItemDataView> itemsCollection;
		/// <summary>
		/// Gets the items collection.
		/// </summary>
		/// <value>The items collection.</value>
		public IList<ItemDataView> ItemsCollection
		{
			get { return itemsCollection; }
			private set
			{
				itemsCollection = value;
				NotifyPropertyChanged("ItemsCollection");
			}
		}
		#endregion Items

		#region RangeAreas
		IList<RangeAreaDataView> rangeAreasCollection;
		/// <summary>
		/// Gets the range areas collection.
		/// </summary>
		/// <value>The range areas collection.</value>
		public IList<RangeAreaDataView> RangeAreasCollection
		{
			get { return rangeAreasCollection; }
			private set
			{
				rangeAreasCollection = value;
				NotifyPropertyChanged("RangeAreasCollection");
			}
		}
		#endregion RangeAreas

		#region Commands
		#region CreateRangeAreaCommand
		RelayCommand createRangeAreaCommand;
		/// <summary>
		/// Gets the CreateRangeAreaCommand.
		/// </summary>
		/// <value>CreateRangeAreaCommand.</value>
		public ICommand CreateRangeAreaCommand
		{
			get
			{
				if (createRangeAreaCommand == null)
				{
					createRangeAreaCommand = new RelayCommand(param => this.CreateRangeArea(param),
						param => this.CanCreateRangeArea(param) );
				}
				return createRangeAreaCommand;
			}
		}

		/// <summary>
		/// <see cref="CreateRangeAreaCommand"/> CanExecute handler.
		/// </summary>
		/// <param name="parameter">List of <see cref="ItemDataView"/> items selected
		/// in the Items ListBox.</param>
		/// <returns>
		/// 	<c>true</c> if new RangeArea can be created; otherwise, <c>false</c>.
		/// </returns>
		/// <remarks>
		/// Determines whether new RangeArea can be created.
		/// </remarks>
		public bool CanCreateRangeArea(object parameter)
		{
			if (parameter == null)
				return false;
			Debug.Assert(parameter is IEnumerable, "parameter is IEnumerable");
			IList<ItemDataView> selected = (parameter as IEnumerable).Cast<ItemDataView>().ToList();
			if (selected.Count != 2)
				return false;
			foreach (RangeAreaDataView rangeArea in RangeAreasCollection)
			{
				if ((rangeArea.ItemDataView1 == selected[0] && rangeArea.ItemDataView2 == selected[1])
					|| (rangeArea.ItemDataView1 == selected[1] && rangeArea.ItemDataView2 == selected[0]))
					return false;
			}
			return true;
		}

		/// <summary>
		/// <see cref="CreateRangeAreaCommand"/> Executed handler.
		/// </summary>
		/// <param name="parameter">List of two <see cref="ItemDataView"/> items selected
		/// in the Items ListBox.</param>
		/// <remarks>
		/// Creates the range area.
		/// </remarks>
		public void CreateRangeArea(object parameter)
		{
			Debug.Assert(parameter is IEnumerable, "parameter is IEnumerable");
			IList<ItemDataView> selected = (parameter as IEnumerable).Cast<ItemDataView>().ToList();
			Debug.Assert(selected.Count == 2, "selected.Count == 2");
			DataViewCollection.Add(new RangeAreaDataView()
				{
					ItemDataView1 = selected[0],
					ItemDataView2 = selected[1],
				});
		}
		#endregion CreateRangeAreaCommand

		#region DeleteRangeAreaCommand
		RelayCommand deleteRangeAreaCommand;
		/// <summary>
		/// Gets the DeleteRangeAreaCommand.
		/// </summary>
		/// <value>DeleteRangeAreaCommand.</value>
		public ICommand DeleteRangeAreaCommand
		{
			get
			{
				if (deleteRangeAreaCommand == null)
				{
					deleteRangeAreaCommand = new RelayCommand(param => this.DeleteRangeArea(param),
						param => this.CanDeleteRangeArea(param) );
				}
				return deleteRangeAreaCommand;
			}
		}

		/// <summary>
		/// <see cref="DeleteRangeAreaCommand"/> CanExecute handler.
		/// </summary>
		/// <param name="parameter">List of <see cref="RangeAreaDataView"/> items selected
		/// in the RangeAreas ListBox.</param>
		/// <returns>
		/// 	<c>true</c> if the selected RangeAreas can be deleted; otherwise, <c>false</c>.
		/// </returns>
		/// <remarks>
		/// Determines whether the selected RangeAreas can be deleted.
		/// </remarks>
		public bool CanDeleteRangeArea(object parameter)
		{
			if (parameter == null)
				return false;
			Debug.Assert(parameter is IList, "parameter is IList");
			IList selected = parameter as IList;
			if (selected.Count == 0)
				return false;
			return true;
		}

		/// <summary>
		/// <see cref="DeleteRangeAreaCommand"/> Executed handler.
		/// </summary>
		/// <param name="parameter">List of <see cref="RangeAreaDataView"/> items selected
		/// in the RangeAreas ListBox.</param>
		/// <remarks>
		/// Deletes the range areas selected.
		/// </remarks>
		public void DeleteRangeArea(object parameter)
		{
			Debug.Assert(parameter is IEnumerable, "parameter is IEnumerable");
			IList<RangeAreaDataView> selected = (parameter as IEnumerable).Cast<RangeAreaDataView>().ToList();
			foreach (RangeAreaDataView item in selected)
			{
				DataViewCollection.Remove(item);
			}
		}
		#endregion DeleteRangeAreaCommand

		#region ChooseRangeAreaColorCommand
		RelayCommand chooseRangeAreaColorCommand;
		/// <summary>
		/// Gets the ChooseRangeAreaColorCommand.
		/// </summary>
		/// <value>ChooseRangeAreaColorCommand.</value>
		public ICommand ChooseRangeAreaColorCommand
		{
			get
			{
				if (chooseRangeAreaColorCommand == null)
				{
					chooseRangeAreaColorCommand = new RelayCommand(
						param => this.ChooseRangeAreaColor(param),
						param => this.CanChooseRangeAreaColor(param));
				}
				return chooseRangeAreaColorCommand;
			}
		}

		/// <summary>
		/// <see cref="ChooseRangeAreaColorCommand"/> CanExecute handler.
		/// </summary>
		/// <param name="parameter">List of <see cref="RangeAreaDataView"/> items selected
		/// in the RangeAreas ListBox.</param>
		/// <returns>
		/// 	<c>true</c> if the selected RangeAreas can be deleted; otherwise, <c>false</c>.
		/// </returns>
		/// <remarks>
		/// Determines whether the selected RangeAreas can be deleted.
		/// </remarks>
		public bool CanChooseRangeAreaColor(object parameter)
		{
			if (parameter == null)
				return false;
			Debug.Assert(parameter is IList, "parameter is IList");
			IList selected = parameter as IList;
			if (selected.Count == 1)
				return true;
			return false;
		}

		/// <summary>
		/// <see cref="ChooseRangeAreaColorCommand"/> Executed handler.
		/// </summary>
		/// <param name="parameter">List of <see cref="RangeAreaDataView"/> items selected
		/// in the RangeAreas ListBox.</param>
		/// <remarks>
		/// Deletes the range areas selected.
		/// </remarks>
		public void ChooseRangeAreaColor(object parameter)
		{
			Debug.Assert(parameter is IEnumerable, "parameter is IEnumerable");
			IList<RangeAreaDataView> selected = (parameter as IEnumerable).Cast<RangeAreaDataView>().ToList();
			Debug.Assert(selected.Count == 1, "selected.Count == 1");
			RangeAreaDataView rangeArea = selected[0];

			Color color = Colors.Black;
			SolidColorBrush brush = rangeArea.AreaBrush as SolidColorBrush;
			if (brush != null)
				color = brush.Color;

			ChooseColorDialog.ColorDialog dlg = new ChooseColorDialog.ColorDialog(color);
			if (dlg.ShowDialog() == true)
				rangeArea.AreaBrush = new SolidColorBrush(dlg.SelectedColor);
		}
		#endregion ChooseRangeAreaColorCommand
		#endregion Commands

		#region INotifyPropertyChanged Members
		public event PropertyChangedEventHandler PropertyChanged;

		void NotifyPropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}
		#endregion INotifyPropertyChanged Members
	}
}
